package com.punch.user.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.punch.common.dto.user.student.StudentDTO;
import com.punch.common.dto.user.student.StudentImportDTO;
import com.punch.common.dto.user.user.RedisUserDTO;
import com.punch.common.enums.course.GradeEnum;
import com.punch.common.enums.user.role.UserRoleEnum;
import com.punch.common.exception.BusinessException;
import com.punch.common.feign.FeignAreaService;
import com.punch.common.feign.FeignOrderService;
import com.punch.common.page.PageInfo;
import com.punch.common.query.user.student.StudentForAppQueryInfo;
import com.punch.common.query.user.student.StudentQueryInfo;
import com.punch.common.simple.redis.utils.UserUtil;
import com.punch.common.tools.BasePage;
import com.punch.common.utils.HulToolExcelUtil;
import com.punch.common.utils.StringUtil;
import com.punch.common.vo.order.SimpleCourseOrderVO;
import com.punch.common.vo.user.student.StudentPageForAppVO;
import com.punch.common.vo.user.student.StudentPageVO;
import com.punch.user.excel.StudentClassExcel;
import com.punch.user.exception.UserExceptionEnum;
import com.punch.user.mapper.SysStudentMapper;
import com.punch.user.model.SysStudent;
import com.punch.user.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
@Slf4j
public class StudentServiceImpl extends ServiceImpl<SysStudentMapper, SysStudent> implements StudentService {


    @Resource
    private FeignOrderService feignOrderService;

    @Resource
    private FeignAreaService feignAreaService;

    @Value("${excel.template.student}")
    private String excelTemplateStudent;

    /**
     * 学生分页列表
     *
     * @param queryInfo 查询信息
     * @return PageInfo<StudentPageVO>
     */
    @Override
    public PageInfo<StudentPageVO> queryPage(StudentQueryInfo queryInfo) {
        RedisUserDTO loginUser = UserUtil.getLoginUser();
        //学校查看自己创建的学生
        queryInfo.setCurrentAreaId(loginUser.getAreaId());
        if (loginUser.getRoleId() == UserRoleEnum.SCHOOL.getCode()) {
//            queryInfo.setAreaId(loginUser.getAreaId());
            queryInfo.setCreateId(loginUser.getUserId());
        }
        return BasePage.queryBasePage(queryInfo, params -> baseMapper.queryCount(params),
                params -> baseMapper.queryList(params));
    }

    /**
     * 新增学生
     *
     * @param studentDTO 学生表单
     * @return 学生id
     */
    @Override
    public Long saveStudent(StudentDTO studentDTO) {
        SysStudent sysStudent = new SysStudent();
        BeanUtils.copyProperties(studentDTO, sysStudent);
        RedisUserDTO loginUser = UserUtil.getLoginUser();
        Integer areaId = loginUser.getAreaId();
        sysStudent.setAreaId(areaId);
        String parentIds = feignAreaService.getParentIds(areaId);
        sysStudent.setAllAreaIds(parentIds + "," + areaId);
        sysStudent.setAreaName(loginUser.getAreaName());
        sysStudent.setSchoolName(loginUser.getUsername());
        sysStudent.setCreateId(loginUser.getUserId());
        boolean flag = save(sysStudent);
        if (!flag) {
            throw new BusinessException(UserExceptionEnum.DATA_INSERT_EXCEPTION);
        }
        return sysStudent.getId();
    }

    /**
     * 修改学生
     *
     * @param studentDTO 学生表单
     */
    @Override
    public void updateStudent(StudentDTO studentDTO) {
        SysStudent sysStudentEX = getById(studentDTO.getStudentId());
        Optional.ofNullable(sysStudentEX).orElseThrow(() -> new BusinessException("学生不存在"));
        SysStudent student = new SysStudent();
        BeanUtils.copyProperties(studentDTO, student);
        student.setId(studentDTO.getStudentId());
        boolean flag = updateById(student);
        if (!flag) {
            throw new BusinessException(UserExceptionEnum.STUDENT_NOT_EXIST);
        }
    }

    /**
     * 删除学生
     *
     * @param studentId 学生id
     */
    @Override
    public void deleteStudent(Long studentId) {
        //todo 学生如果有参加课程订单 不能删除
        boolean flag = removeById(studentId);
        if (!flag) {
            throw new BusinessException(UserExceptionEnum.STUDENT_NOT_EXIST);
        }
    }

    /**
     * 学生启用/停用
     *
     * @param studentId 学生id
     */
    @Override
    public void enableStudent(Long studentId) {
        SysStudent sysStudentEX = getById(studentId);
        if (sysStudentEX == null) {
            throw new BusinessException(UserExceptionEnum.STUDENT_NOT_EXIST);
        }
        SysStudent updateStudent = new SysStudent();
        //启用变成停用
        if (sysStudentEX.getEnabled() == 1) {
            updateStudent.setEnabled(0);
        }
        //停用变成启用
        if (sysStudentEX.getEnabled() == 0) {
            updateStudent.setEnabled(1);
        }
        updateStudent.setId(studentId);
        updateById(updateStudent);
    }

    /**
     * 学生导入
     *
     * @param file 文件流
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void importStudent(MultipartFile file) {
        try {
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
            Map<String, String> headerAlias = HulToolExcelUtil.createExcelHeader(StudentImportDTO.class);
            reader.setHeaderAlias(headerAlias);
            List<StudentImportDTO> importStudentList = reader.readAll(StudentImportDTO.class);
            List<SysStudent> insertList = new ArrayList<>();
            RedisUserDTO loginUser = UserUtil.getLoginUser();
            DateTimeFormatter emptyFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            DateTimeFormatter horizontalFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            DateTimeFormatter backslashFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
            importStudentList.forEach(studentImportDTO -> {
                SysStudent sysStudent = new SysStudent();
                sysStudent.setName(StringUtil.replaceBlank(studentImportDTO.getName()));
                String birthDay = StringUtil.replaceBlank(studentImportDTO.getBirthday().replaceAll(" 00:00:00", ""));
                log.info("studentImportDTO.getBirthday()=={}", birthDay);
                if (birthDay.contains("-")) {
                    sysStudent.setBirthday(LocalDate.parse(birthDay, horizontalFormatter));
                } else if (birthDay.contains("/")) {
                    sysStudent.setBirthday(LocalDate.parse(birthDay, backslashFormatter));
                } else {
                    sysStudent.setBirthday(LocalDate.parse(birthDay, emptyFormatter));
                }
                sysStudent.setGradeYear(GradeEnum.getCode(studentImportDTO.getGradeYear()));
                sysStudent.setGradeClass(studentImportDTO.getGradeClass());
                int sex;
                if ("女".equals(studentImportDTO.getSex())) {
                    sex = 0;
                } else if ("男".equals(studentImportDTO.getSex())) {
                    sex = 1;
                } else {
                    sex = 2;
                }
                sysStudent.setSex(sex);
                sysStudent.setGuardianMobile(StringUtil.replaceBlank(studentImportDTO.getGuardianMobile()));
                sysStudent.setAreaId(loginUser.getAreaId());
                sysStudent.setAreaName(loginUser.getAreaName());
                sysStudent.setSchoolName(loginUser.getUsername());
                sysStudent.setCreateId(loginUser.getUserId());
                insertList.add(sysStudent);
            });
            log.info("insertList:{}", JSON.toJSON(insertList));
//            saveBatch(insertList);
        } catch (IOException e) {
            log.error("读取文件错误:{}", e.getMessage());
        }
    }

    /**
     * 学校年级下学生报班信息导出
     *
     * @param schoolName 学校名称
     * @param gradeYear  年级
     * @param gradeClass 班级
     * @param className  课程id
     * @param response   response
     */
    @Override
    public void exportClassStudent(String schoolName, Integer gradeYear, String gradeClass, String className, HttpServletResponse response) throws IOException {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String file = GradeEnum.getName(gradeYear) + gradeClass + "学生报名信息.xlsx";
            String fileName = URLEncoder.encode(file, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(excelTemplateStudent).excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            //获取学生信息
            List<StudentClassExcel> stuList = new ArrayList<>();
            fillExcelStudent(stuList, schoolName, gradeYear, gradeClass, className);
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            excelWriter.fill(stuList, fillConfig, writeSheet);
            excelWriter.finish();
        } catch (Exception e) {
            log.error("导出文件错误,{}", e.getMessage());
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    /**
     * 远程调用学生列表
     *
     * @param schoolId    学校id
     * @param studentName 学生姓名
     * @param currPage
     * @param pageSize
     * @return List<StudentPageVO>
     */
    @Override
    public PageInfo<StudentPageVO> feignStudentList(Long schoolId, String studentName, Integer currPage, Integer pageSize) {
        StudentQueryInfo queryInfo = new StudentQueryInfo();
        queryInfo.setCurrPage(currPage);
        queryInfo.setPageSize(pageSize);
        queryInfo.setStudentName(studentName);
        queryInfo.setCreateId(schoolId);
        return BasePage.queryBasePage(queryInfo, params -> baseMapper.queryCount(params),
                params -> baseMapper.queryList(params));
    }

    @Override
    public void handlerStudentArea() {
        List<SysStudent> list = list();
        list.forEach((t) -> {
            String parentIds = feignAreaService.getParentIds(t.getAreaId());
            SysStudent sysStudent = new SysStudent();
            sysStudent.setAllAreaIds(parentIds + "," + t.getAreaId());
            sysStudent.setId(t.getId());
            updateById(sysStudent);
        });
    }

    /**
     * 学校信息
     *
     * @param stuList    学生列表
     * @param schoolName 学校名称
     * @param gradeYear  年级
     * @param gradeClass 班级
     * @param className  报班名称
     */
    private void fillExcelStudent(List<StudentClassExcel> stuList, String schoolName, Integer gradeYear, String gradeClass, String className) {
        QueryWrapper<SysStudent> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().like(SysStudent::getSchoolName, schoolName).eq(SysStudent::getGradeYear, gradeYear).eq(SysStudent::getGradeClass, gradeClass);
        List<SimpleCourseOrderVO> stuOrderList = feignOrderService.getSimpleCourseClassOrder(schoolName, GradeEnum.getName(gradeYear), gradeClass, className);
        StringBuilder classNameStr = new StringBuilder();
        list(queryWrapper).forEach(sysStudent -> {
            StudentClassExcel studentSignUpExcel = new StudentClassExcel();
            studentSignUpExcel.setStudentName(sysStudent.getName());
            studentSignUpExcel.setGradeClass(sysStudent.getGradeClass());
            studentSignUpExcel.setGradeYear(GradeEnum.getName(sysStudent.getGradeYear()));
            studentSignUpExcel.setStudentSex(sysStudent.getSex() == 0 ? "女" : "男");
            studentSignUpExcel.setGuardianMobile(sysStudent.getGuardianMobile());
            classNameStr.delete(0, classNameStr.length());
            stuOrderList.forEach(stuOrder -> {
                if (sysStudent.getId().equals(stuOrder.getStudentId())) {
                    classNameStr.append(stuOrder.getCourseClassName()).append(",");
                }
            });
            if (StrUtil.isNotEmpty(classNameStr)) {
                classNameStr.deleteCharAt(classNameStr.length() - 1);
                studentSignUpExcel.setCourseClasses(classNameStr.toString());
            } else {
                studentSignUpExcel.setCourseClasses("");
            }
            stuList.add(studentSignUpExcel);
        });
    }

    /**
     * 学生分页列表
     *
     * @param studentForAppQueryInfo 查询信息
     * @return PageInfo<StudentPageVO>
     */
    @Override
    public PageInfo<StudentPageForAppVO> queryPageForApp(StudentForAppQueryInfo studentForAppQueryInfo) {
        return BasePage.queryBasePage(studentForAppQueryInfo, params -> baseMapper.queryCount(params),
                params -> {
                    List<StudentPageForAppVO> retList = baseMapper.queryListForApp(params);
                    retList.forEach(this::accept);
                    return retList;
                });
    }

    private void accept(StudentPageForAppVO studentPageForAppVO) {
        studentPageForAppVO.setGradeYear(GradeEnum.getName(Integer.valueOf(studentPageForAppVO.getGradeYear())));
    }

}
